package bootstrap

import (
	"context"
	"errors"
	"fmt"
	"github.com/spf13/cobra"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/rest"
	"github.com/zeromicro/go-zero/rest/httpx"
	"insighttracking/apps/customer/internal/config"
	"insighttracking/apps/customer/internal/handler"
	"insighttracking/apps/customer/internal/svc"
	"insighttracking/common/utils"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"github.com/zeromicro/go-zero/core/conf"
)

var Start = &cobra.Command{Use: "start"}

func init() {
	var yaml string
	var srvCtx *svc.ServiceContext
	var c config.Config
	var server *rest.Server
	Start.Flags().StringVar(&yaml, "config", "etc/customer-api.yaml", "setting config file")
	Start.PreRunE = func(cmd *cobra.Command, args []string) error {
		conf.MustLoad(yaml, &c)
		srvCtx = svc.NewServiceContext(c)
		// 处理跨域
		server = rest.MustNewServer(c.RestConf, rest.WithCors())
		handler.RegisterHandlers(server, srvCtx)
		return nil
	}
	Start.PersistentPostRunE = func(cmd *cobra.Command, args []string) (err error) {
		db, _ := srvCtx.MysqlDb.DB()
		err = db.Close()
		srvCtx.RedisDb.Shutdown(context.Background())
		err = logx.Close()
		return
	}
	Start.RunE = func(cmd *cobra.Command, args []string) (err error) {
		var listener net.Listener
		gracefulKey := "customer-api-graceful"
		gracefulVal := os.Getenv(gracefulKey)

		switch gracefulVal {
		case "TRUE":
			err = os.Unsetenv(gracefulKey)
			listener, err = net.FileListener(os.NewFile(3, ""))
		default:
			listener, err = net.Listen("tcp", ":"+strconv.Itoa(c.Port))
		}
		if err != nil {
			fmt.Println("listen err:", err)
			return
		}
		defer func(listener net.Listener) {
			err = listener.Close()
		}(listener)

		srv := &http.Server{}
		go func() {
			httpx.SetOkHandler(utils.OkHandler)
			httpx.SetErrorHandlerCtx(utils.ErrHandler(c.Name))
			srv.Handler = server
			if err = srv.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
				logx.Infof("service start failed: %s\n", err)
			}
			fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
		}()

		fmt.Println("graceful setting")
		sigs := make(chan os.Signal, 1)
		signal.Notify(sigs, syscall.SIGINT, syscall.SIGHUP)
		switch <-sigs {
		case syscall.SIGHUP:
			fmt.Println("receive restart signal")
			lf, err := listener.(*net.TCPListener).File()
			if err != nil {
				logx.Errorf("get listener file failed: %s\n", err)
			}

			err = os.Setenv(gracefulKey, "TRUE")
			attr := &os.ProcAttr{Env: os.Environ(), Files: []*os.File{os.Stdin, os.Stdout, os.Stderr, lf}}
			_, err = os.StartProcess(os.Args[0], os.Args, attr)
			if err != nil {
				logx.Errorf("start: start process failed: %s\n", err)
			}
		case syscall.SIGINT:
			fmt.Println("receive stop signal")
			logx.Info("receive stop signal")
		}

		ctx, cancel := context.WithTimeout(context.TODO(), time.Second*30)
		defer cancel()

		if err = srv.Shutdown(ctx); err != nil {
			logx.Errorf("start: http service shutdown failed: %s\n", err)
			return
		}

		logx.Infof("service exited pid: %v\n", os.Getpid())
		return
	}
}
